/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frstat_ct.h                                        */
/*  NOTE      =                                                    */
/*  DATE      = 2015/01/15 by yaohl                                */
/*******************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#include "frcomm.h"
#include "frlog.h"
#include "frmem.h"
#include "frhash2.h"
#include "conntrack.h"
#include "frct_comm.h"
#include "frct_hash.h"
#include "frct_mng.h"
#include "frcmd_server.h"
#include "frame_params.h"

#define APM_L7_STAT_HASH_SIZE  100

#define APM_L7_PROTOCOL_ALL         -1      /* 所有协议 */
#define APM_L7_PROTOCOL_UNKNOWN     -2      /* 未识别协议 */

typedef struct __st_ct_conn_stat_l7 {
    int i_l7_proto;
    unsigned int ui_all_conn_count;
    unsigned int ui_dead_conn_count;

} Frct_conn_stat_l7;


typedef struct __st_stat_all {
    unsigned int ui_all_conn_count;
    unsigned int ui_dead_conn_count;

    //l7 协议分组统计信息
    Frhash_header *p_st_hash_l7;
} Fr_stat_all;

typedef struct __st_stat_l7 {
    Frct_conn_stat_l7 st_conn_stat_l7;

    int i_idx;

    int i_flag_use_sock;
    int accept_sock;

    char *p_buff_proto;
    int i_buff_proto_size;

    char *p_buff_ct;
    int i_buff_ct_size;
} Fr_stat_l7;

/*******************************************************************************
*  FUNC     :  节点内存释放
*  ARGS     :  p_st_node  (Frlist_node *)
*  RTN      :
*  NOTE     :
*******************************************************************************/
static void __hash_node_conn_stat_free(Frlist_node *p_st_node)
{
    if(p_st_node==NULL || p_st_node->data == NULL)
        return;

    frfree(p_st_node->data);
    return;
}

/*******************************************************************************
*  FUNC     :  增加l7连接统计信息
*  ARGS     :  p_st_hash_l7       (Frhash_header *)
*           :  i_l7_proto         (l7 proto)
*           :  i_dead_conn        (僵尸连接个数)
*  RTN      :  返回值为0时, 终止运行
*  NOTE     :
*******************************************************************************/
static void __frhash_stat_l7_add(Frhash_header *p_st_hash_l7, int i_l7_proto, int i_dead_conn)
{
    Frlist_node* p_st_node;
    Frct_conn_stat_l7 *p_st_conn_stat_l7;

    if(p_st_hash_l7 == NULL)
        return;

    //查询l7相应的节点
    p_st_node = frhash2_getLong(p_st_hash_l7, i_l7_proto);
    if(p_st_node && p_st_node->data)
    {
        p_st_conn_stat_l7 = (Frct_conn_stat_l7 *)(p_st_node->data);
        p_st_conn_stat_l7->ui_all_conn_count++;
        p_st_conn_stat_l7->ui_dead_conn_count += i_dead_conn;
        return;
    }

    //生成新的节点
    p_st_conn_stat_l7 = frmalloc(sizeof(Frct_conn_stat_l7));
    if(p_st_conn_stat_l7 == NULL)
        return;
    memset(p_st_conn_stat_l7, 0x00, sizeof(Frct_conn_stat_l7));
    p_st_conn_stat_l7->i_l7_proto = i_l7_proto;
    p_st_conn_stat_l7->ui_all_conn_count = 1;
    p_st_conn_stat_l7->ui_dead_conn_count = i_dead_conn;

    p_st_node = frlist_node_create(p_st_conn_stat_l7);
    if(p_st_node == NULL)
    {
        frfree(p_st_conn_stat_l7);
        return;
    }

    frhash2_addLong(p_st_hash_l7, i_l7_proto, p_st_node);
    return;
}

/*******************************************************************************
*  FUNC     :  run用函数
*  ARGS     :  p_st_node       (Frlist_node *)
            :  p_data          (运算用数据)
*  RTN      :  返回值为0时, 终止运行
*  NOTE     :
*******************************************************************************/
static int __hash_node_all_run(Frlist_node* p_st_node, const void *p_data)
{
    Conntrack *p_st_ct = NULL;
    Fr_stat_all *p_st_stat_all = (Fr_stat_all *)p_data;
    int i_dead_conn=0;
    struct timeval st_timeval;

    if(p_st_node == NULL || p_st_node->data == NULL || p_data == NULL )
        return 1;

    gettimeofday (&st_timeval, NULL);
    p_st_ct = (Conntrack *)(p_st_node->data);
    //精确到秒
    if(st_timeval.tv_sec - p_st_ct->st_connection_lastskb_time.tv_sec > st_apm_params.ui_dead_conn_time)
        i_dead_conn = 1;

    p_st_stat_all->ui_all_conn_count++;
    p_st_stat_all->ui_dead_conn_count += i_dead_conn;

    //防止协议查询过程中，有协议释放
    pthread_spin_lock(&p_st_ct->lock);
    if(frlist_size(&p_st_ct->lst_hdr_l7proto) <= 0)
    {
        __frhash_stat_l7_add( p_st_stat_all->p_st_hash_l7, APM_L7_PROTOCOL_UNKNOWN, i_dead_conn);
        pthread_spin_unlock(&p_st_ct->lock);
        return 1;
    }

    {
        Frlist_node *p_st_node_l7_proto=NULL;
        L7_analyzer *p_st_l7_analyzer=NULL;
        p_st_node_l7_proto = FRLIST_FIRST_NODE(&p_st_ct->lst_hdr_l7proto);
        p_st_l7_analyzer =  (L7_analyzer*)FRLIST_GET_NODE_DATA(p_st_node_l7_proto);

        //未识别协议
        if(p_st_l7_analyzer == NULL || p_st_l7_analyzer->p_st_l7_module == NULL)
        {
            __frhash_stat_l7_add( p_st_stat_all->p_st_hash_l7, APM_L7_PROTOCOL_UNKNOWN, i_dead_conn);
        }
        else
        {
            while(p_st_node_l7_proto)
            {
                if(p_st_l7_analyzer && p_st_l7_analyzer->p_st_l7_module)
                    __frhash_stat_l7_add( p_st_stat_all->p_st_hash_l7, p_st_l7_analyzer->p_st_l7_module->i_l7_proto, i_dead_conn);
                p_st_node_l7_proto = FRLIST_NEXT_NODE(p_st_node_l7_proto);
                p_st_l7_analyzer =  (L7_analyzer*)FRLIST_GET_NODE_DATA(p_st_node_l7_proto);
            }
        }
    }
    pthread_spin_unlock(&p_st_ct->lock);

    return 1;
}

static void __frstat_l7proto_str(int i_l7_proto, char *p_outbuff, unsigned int ui_buff_size)
{
    if(p_outbuff == NULL || ui_buff_size <=0)
        return;

    switch(i_l7_proto)
    {
        case APM_L7_PROTOCOL_ALL:
            snprintf(p_outbuff, ui_buff_size, "all");
            break;
        case APM_L7_PROTOCOL_UNKNOWN:
            snprintf(p_outbuff, ui_buff_size, "unknown");
            break;
        default:
            snprintf(p_outbuff, ui_buff_size, "%d", i_l7_proto);
            break;
    }
    return;
}

int frstat_ct_all(char **pp_result, int accept_sock)
{
    Fr_stat_all st_all_conn_stat = {0,0, NULL};
    Frct_conn_stat_l7 *p_st_conn_stat_l7;
    int i_loop=0;
    Frlist_header *p_st_lst_hdr = NULL;
    Frlist_node *p_st_node;
    Frstr *p_st_str=NULL;
    char arr_buff[128], arr_l7_proto[20];

    st_all_conn_stat.p_st_hash_l7 = frhash2_create2(APM_L7_STAT_HASH_SIZE);
    if(st_all_conn_stat.p_st_hash_l7 == NULL)
        return FR_FAIL;

    p_st_str=frstr_new(1024);
    if(p_st_str == NULL)
    {
        frhash2_free2(st_all_conn_stat.p_st_hash_l7, __hash_node_conn_stat_free);
        return FR_FAIL;
    }

    frct_del_disable();
    frct_hash_run( &st_all_conn_stat, __hash_node_all_run);
    frct_del_enable();

    //总连接数、并发连接数、僵尸连接数
    snprintf(arr_buff, sizeof(arr_buff), "total conns:%u\tdead conns:%u\n"
            , st_all_conn_stat.ui_all_conn_count, st_all_conn_stat.ui_dead_conn_count);
    frstr_append(p_st_str, arr_buff, strlen(arr_buff));

    //按L7协议分组统计的连接数、并发连接数、僵尸连接数
    FRHASH_LOOP(i_loop,st_all_conn_stat.p_st_hash_l7, p_st_lst_hdr)
    {
        p_st_node = FRLIST_FIRST_NODE(p_st_lst_hdr);
        while(p_st_node)
        {
            if(p_st_node && p_st_node->data)
            {
                p_st_conn_stat_l7 = (Frct_conn_stat_l7 *)(p_st_node->data);

                arr_l7_proto[0] = '\0';
                __frstat_l7proto_str(p_st_conn_stat_l7->i_l7_proto, arr_l7_proto, sizeof(arr_l7_proto));
                snprintf(arr_buff, sizeof(arr_buff), "l7:%s\ttotal conns:%u\tdead conns:%u\n"
                        , arr_l7_proto
                        , p_st_conn_stat_l7->ui_all_conn_count
                        , p_st_conn_stat_l7->ui_dead_conn_count);
                frstr_append(p_st_str, arr_buff, strlen(arr_buff));
            }
            p_st_node = FRLIST_NEXT_NODE(p_st_node);
        }
    }

    if(pp_result == NULL)
    {
        frdbg("==========================\n");
        printf("output ct info\n%s\n", p_st_str->p_str);
        frdbg("==========================\n");
        frstr_free(&p_st_str);
    }
    else
        *pp_result = frstr_free2(&p_st_str);

    frhash2_free2(st_all_conn_stat.p_st_hash_l7, __hash_node_conn_stat_free);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  run用函数
*  ARGS     :  p_st_node       (Frlist_node *)
            :  p_data          (运算用数据)
*  RTN      :  返回值为0时, 终止运行
*  NOTE     :
*******************************************************************************/
static int __hash_node_l7_run(Frlist_node* p_st_node, const void *p_data)
{
    Conntrack *p_st_ct = NULL;
    Fr_stat_l7 *p_st_stat_l7 = (Fr_stat_l7 *)p_data;
    int i_dead_conn=0, i_flag_prt=FR_NO;
    struct timeval st_timeval;
    char arr_ip_src[56], arr_ip_dst[56], arr_createtime[56];
    int i_len=0;
    Frlist_node *p_st_node_l7_proto=NULL;
    L7_analyzer *p_st_l7_analyzer=NULL;

    if(p_st_node == NULL || p_st_node->data == NULL || p_data == NULL )
        return 1;

    gettimeofday (&st_timeval, NULL);
    p_st_ct = (Conntrack *)(p_st_node->data);
    //精确到秒
    if(st_timeval.tv_sec - p_st_ct->st_connection_lastskb_time.tv_sec > st_apm_params.ui_dead_conn_time)
        i_dead_conn = 1;

    //防止协议查询过程中，有协议释放
    pthread_spin_lock(&p_st_ct->lock);
    //未知协议
    if(p_st_stat_l7->st_conn_stat_l7.i_l7_proto == APM_L7_PROTOCOL_UNKNOWN
       && frct_l7proto_is_last2(p_st_ct, 0) == FR_SUCC )
    {
        p_st_node_l7_proto = FRLIST_FIRST_NODE(&p_st_ct->lst_hdr_l7proto);
        p_st_l7_analyzer =  (L7_analyzer*)FRLIST_GET_NODE_DATA(p_st_node_l7_proto);

        if(p_st_l7_analyzer == NULL || p_st_l7_analyzer->p_st_l7_module == NULL)
        {
            p_st_stat_l7->st_conn_stat_l7.ui_all_conn_count++;
            p_st_stat_l7->st_conn_stat_l7.ui_dead_conn_count += i_dead_conn;
            i_flag_prt = FR_YES;
        }
    }
    else //指定协议或所有协议
    {
        p_st_node_l7_proto = FRLIST_FIRST_NODE(&p_st_ct->lst_hdr_l7proto);
        while(p_st_node_l7_proto)
        {
            p_st_l7_analyzer =  (L7_analyzer*)FRLIST_GET_NODE_DATA(p_st_node_l7_proto);

            switch(p_st_stat_l7->st_conn_stat_l7.i_l7_proto)
            {
                case APM_L7_PROTOCOL_ALL:
                    p_st_stat_l7->st_conn_stat_l7.ui_all_conn_count++;
                    p_st_stat_l7->st_conn_stat_l7.ui_dead_conn_count += i_dead_conn;
                    i_flag_prt = FR_YES;
                    break;
                default:
                    if(p_st_l7_analyzer && p_st_l7_analyzer->p_st_l7_module
                       && p_st_l7_analyzer->p_st_l7_module->i_l7_proto == p_st_stat_l7->st_conn_stat_l7.i_l7_proto )
                    {
                        p_st_stat_l7->st_conn_stat_l7.ui_all_conn_count++;
                        p_st_stat_l7->st_conn_stat_l7.ui_dead_conn_count += i_dead_conn;
                        i_flag_prt = FR_YES;
                    }
                    break;
            }
            p_st_node_l7_proto = FRLIST_NEXT_NODE(p_st_node_l7_proto);
        }
    }

    if(i_flag_prt == FR_NO)
    {
        pthread_spin_unlock(&p_st_ct->lock);
        return 1;
    }

    p_st_stat_l7->i_idx++;
    p_st_stat_l7->p_buff_proto[0] = '\0';
    frlist_show( &p_st_ct->lst_hdr_l7proto, p_st_stat_l7->p_buff_proto, p_st_stat_l7->i_buff_proto_size, frct_l7proto_node_show);
    i_len = strlen(p_st_stat_l7->p_buff_proto);
    //去除最后的,
    if(i_len > 0)
        p_st_stat_l7->p_buff_proto[i_len-1] = '\0';

    snprintf(arr_ip_src, sizeof(arr_ip_src), "%s", int_ntoa(p_st_ct->src_ip));
    snprintf(arr_ip_dst, sizeof(arr_ip_dst), "%s", int_ntoa(p_st_ct->dst_ip));
    frtime2str(&p_st_ct->st_connection_created, arr_createtime, sizeof(arr_createtime));
    snprintf(p_st_stat_l7->p_buff_ct, p_st_stat_l7->i_buff_ct_size
           , "%05d %s %d(%s) src=%s sport=%i dst=%s dport=%i create_time=[%s] queue=%u peek=%u rx=%lu rx_byte=%lu tx=%lu tx_byte=%lu drops=%lu refcnt=%u l7proto_status=%s %s\n"
           , p_st_stat_l7->i_idx
           , frct_status(p_st_ct->flg_status)
           , p_st_ct->ip_proto, FR_L4PROTO2STR(p_st_ct->ip_proto)
           , arr_ip_src, p_st_ct->src_port, arr_ip_dst, p_st_ct->dst_port
           , arr_createtime
           , frlist_size(&p_st_ct->lst_cap_queue) , frlist_size(&p_st_ct->lst_cap_peek)
           , p_st_ct->ul_rx_packets, p_st_ct->ul_rx_bytes, p_st_ct->ul_tx_packets, p_st_ct->ul_tx_bytes, p_st_ct->ul_drop_packets
           , p_st_ct->refcnt
           , frct_l7status(p_st_ct->i_flag_l7proto_status), p_st_stat_l7->p_buff_proto);
    pthread_spin_unlock(&p_st_ct->lock);

    if(p_st_stat_l7->i_flag_use_sock == FR_YES)
    {
        if (frcmd_send_data(p_st_stat_l7->accept_sock, p_st_stat_l7->p_buff_ct, strlen(p_st_stat_l7->p_buff_ct)) != 0)
        {
            //发送失败，终止发送程序
            FRLOG_ERROR_DETAIL("print ct send data failed\n");
            return 0;
        }
    }
    else
        printf("%s\n", p_st_stat_l7->p_buff_ct);

    return 1;
}

/*******************************************************************************
*  FUNC     :  指定l7的ct信息出力到客户端
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
int frstat_ct_l7(char **pp_result, int i_l7_proto, int accept_sock)
{
    Fr_stat_l7 st_stat_l7;
    int i_ret = FR_SUCC;
    char *p_ct_title = "output ct info\n";
    char arr_stat_l7[128];

    memset(&st_stat_l7, 0x00, sizeof(st_stat_l7));
    if(pp_result)
        st_stat_l7.i_flag_use_sock = FR_YES;
    st_stat_l7.accept_sock = accept_sock;
    st_stat_l7.st_conn_stat_l7.i_l7_proto = i_l7_proto;

    st_stat_l7.i_buff_proto_size = 1024*2;
    st_stat_l7.p_buff_proto = frmalloc(st_stat_l7.i_buff_proto_size);
    st_stat_l7.i_buff_ct_size = 1024*3;
    st_stat_l7.p_buff_ct = frmalloc(st_stat_l7.i_buff_ct_size);
    if(st_stat_l7.p_buff_proto == NULL || st_stat_l7.p_buff_ct == NULL)
    {
        i_ret = FR_FAIL;
        FRLOG_ERROR_DETAIL("frmalloc err");
        goto END;
    }

    if(pp_result == NULL)
    {
        frdbg("==========================\n");
        printf("%s\n", p_ct_title);
    }
    else
        frcmd_send_data(accept_sock, p_ct_title, strlen(p_ct_title));

    frct_del_disable();
    frct_hash_run( &st_stat_l7, __hash_node_l7_run);
    frct_del_enable();

    {
        char arr_l7_proto[20];
        arr_l7_proto[0] = '\0';
        __frstat_l7proto_str(st_stat_l7.st_conn_stat_l7.i_l7_proto, arr_l7_proto, sizeof(arr_l7_proto));

        snprintf(arr_stat_l7, sizeof(arr_stat_l7), "\n\nl7:%s\ttotal conns:%u\tdead conns:%u\n"
                , arr_l7_proto
                , st_stat_l7.st_conn_stat_l7.ui_all_conn_count
                , st_stat_l7.st_conn_stat_l7.ui_dead_conn_count);
    }

    if(pp_result == NULL)
    {
        printf("%s\n", arr_stat_l7);
        frdbg("==========================\n");
    }
    else
        frcmd_send_data(accept_sock, arr_stat_l7, strlen(arr_stat_l7));

END:
    if(st_stat_l7.p_buff_proto)
        frfree(st_stat_l7.p_buff_proto);
    if(st_stat_l7.p_buff_ct)
        frfree(st_stat_l7.p_buff_ct);

    if(i_ret == FR_SUCC && pp_result)
    {
        *pp_result = frmalloc(1);
        (*pp_result)[0] = '\0';
    }
    return i_ret;
}


